//
// FieldsMain.cpp
// libprotea
// Tools Library for the Protea Project / Main implementation of Fields
// Copyright (C) 2003, 2004, 2005 eXegeSys, Inc.
// Copyright (C) 2008 Bruce A. James
//
// This program is part of the Protea Project.
//
// The Protea Project is free software; you can redistribute it and/or modify it 
// under the terms of the GNU General Public License as published by the 
// Free Software Foundation, either version 3 of the License, or (at your 
// option) any later version.
//
// The Protea Project is distributed in the hope that it will be useful, but 
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
// for more details.
// 
// You should have received a copy of the GNU General Public License along 
// with this program.  If not, see <http://www.gnu.org/licenses/>.
// 
// Please send correspondence to:
// 
// theproteaproject@gmail.com
//

//
// FieldsMain
//
#include "../defines.h"
#include "../Buffer/Buffer.h"
#include "../AppHandler/AppInterface.h"
#include "../OSDep/transport.h"
#include "../OSDep/utf8.h"
#include "../Security/base64.h"
#include "fields.h"
#include "field_defns.h"
#include <stdio.h>
#include <time.h>
#include <math.h>
#include <ctype.h>

#include "../defns.h"
using namespace ProteaApp1;

// Why don't Solaris, Windows, IA64 have this function?!?!?!?
double trunc( double value ) {
	if (value > 0)
		return floor( value );
	else
		return ceil( value );
};


XCField::~XCField() {
	if( type == XCBLOB && validblob ) {		// see if we need to delete the BLOB
		delete[] *(long **)(buffer_offset + sizeof( long ));
		validblob = false;
		owner->SetContainsBLOB( false );	// and tell the owner it doesn't have one anymore
	}
	if (tempbuffer)
		delete buffer_offset;
};



XCField::XCField() {
	tempbuffer = false;
	validblob = false;
	count = 0;

	changed			= false;
	truncated		= false;
	read_only		= false;
	desc_message	= 0;
};


XCField::XCField( const XCField& new_field ) {
	cout << "XCField: ByRef constructor" << endl;

	number			= new_field.number;
	allocsize		= new_field.allocsize;
	size			= new_field.size;
//	Position		= new_field.Position;
	dec_places		= new_field.dec_places;
	type			= new_field.type;
	min_len			= new_field.min_len;
	flags			= new_field.flags;
	buffer_offset	= new_field.buffer_offset;
	mydesc			= new_field.mydesc;
	tempbuffer		= new_field.tempbuffer;
	validblob		= new_field.validblob;
	owner			= new_field.owner;
	desc_message	= new_field.desc_message;

	changed			= false;
	truncated		= false;
	read_only		= false;

	if (tempbuffer) {
		count = new_field.count;
		(*count)++;
	} else
		count = NULL;
};



XCField* XCField::Clone() const {
	INFUNC( XCField::Clone, owner->APPDIC );

	XCField* result = new XCField();

	result->number			= number;
	result->allocsize		= allocsize;
	result->size			= size;
	result->dec_places		= dec_places;
	result->type			= type;
	result->min_len			= min_len;
	result->flags			= flags;
	result->mydesc			= mydesc;
	result->field_descr = (buffer_field*)owner->APPDIC->FixOffset( mydesc->field_offset );
	result->owner			= owner;
	result->desc_message	= desc_message;

	// Allocate a temporary buffer
	result->tempbuffer		= true;
//	result->Position		= 0;
	result->buffer_offset	= new char[ allocsize ];

	// Copy the data
	memcpy( result->buffer_offset, buffer_offset, allocsize );

	result->count = NULL;

	OUTFUNCRET( result );
};



void XCField::Release() {
	delete this;
};




int XCField::GetDescrMessage() const {
	return desc_message;
};

int	XCField::GetType() const {
	return type;
};

char* XCField::GetOffset() const {
	return buffer_offset;
};

short int XCField::GetFlags() const {
	return flags;
};

void XCField::SetFlags( short int new_flags ) {
	flags = new_flags;
};

long XCField::GetLength() const {
	return size;
};

long XCField::GetAllocSize() const {
	return allocsize;
};

long XCField::GetDecPlaces()	const {
	return dec_places;
};

long XCField::GetFieldNumber() const {
	return number;
};

char* XCField::GetFieldName() const {
	return field_descr->name;
};

XCBuffer* XCField::GetBuffer() const {
	return owner;
};

bool XCField::GetTruncationFlag() const {
	return truncated;
};


double XCField::GetDouble( bool coerce ) const {
	INFUNC( XCField::GetDouble, owner->APPDIC );

	double result = 0;

	if (type == DEC_DOUBLE)
		result = *(double*)buffer_offset;
	else if (coerce) {
		switch( type ) {
			case DEC_DOUBLE:
				break;

			case DEC_FLOAT:
				result = GetFloat();
				break;

			case S_INTEGER:
				result = GetShort();
				break;

			case D_INTEGER:
				result = GetLong();
				break;

			case DISPLAY_NUM: {
				const char* temp = buffer_offset;
				if (!*temp)
					// Locate the buffer's default value space and copy over our info
					temp = owner->APPDIC->FixOffset( owner->mydesc->default_offset ) + mydesc->data_offset;

				char* stop;
				double tresult = strtod( temp, &stop );
				if (*stop == 0) {
					if (tresult == HUGE_VAL) {
						RAISEFIELDERROR( ERR_OVERFLOW, GetFieldNumber() );
					} else if (tresult == -HUGE_VAL) 
						RAISEFIELDERROR( ERR_UNDERFLOW, GetFieldNumber() );
					result = tresult;
				} else {
					owner->APPDIC->ToolsTrace( TRC_FIELD, TLV_LOW, "Invalid decimal digit\nOriginal string: '%s'\nError at: '%s'", temp, stop );
					RAISEFIELDERROR( ERR_INV_DEC_DIGIT, GetFieldNumber() );
				};
				break;
			};

			// Convert duration type to a decimal value
			case DUR_HH_TIME_DISP:
			case DUR_HM_TIME_DISP:
				result = DurationToDouble();
				break;
				
			default:
				owner->APPDIC->ToolsTrace( TRC_FIELD, TLV_HIGH, "(%d,%d) Invalid type: %d", owner? owner->GetFormatNumber() : -1, number, type );
				RAISEFIELDERROR_3PARAM( ERR_INV_TYPE, GetFieldNumber(), owner? owner->GetFormatNumber() : -1, number, type );
				break;
		};
	} else {
		owner->APPDIC->ToolsTrace( TRC_FIELD, TLV_HIGH, "(%d,%d) Invalid type: %d", owner? owner->GetFormatNumber() : -1, number, type );
		RAISEFIELDERROR_3PARAM( ERR_INV_TYPE, GetFieldNumber(), owner? owner->GetFormatNumber() : -1, number, type );
	};

	OUTFUNCRET( result );
};

float XCField::GetFloat() const {
	return *(float*)buffer_offset;
};

int XCField::GetLong( bool coerce ) const {
	INFUNC( XCField::GetLong, owner->APPDIC )

	int result=0;

	if (type == D_INTEGER || type == LITERAL_VAL || type == AUTONUMBER)
		result = *(long*)buffer_offset;
	else if (coerce) {
		switch( type ) {
			case S_INTEGER:
				result = (int)GetShort();
				break;

			case DEC_FLOAT:
				result = (int)GetFloat();
				break;

			case DEC_DOUBLE:
				result = (int)GetDouble();
				break;

			case XCBOOL:
				result = (int)GetBool();
				break;

			case DISPLAY_NUM: {
				const char* temp = buffer_offset;
				if (!*temp)
					// Locate the buffer's default value space and copy over our info
					temp = owner->APPDIC->FixOffset( owner->mydesc->default_offset ) + mydesc->data_offset;
				result = atoi( temp );
				break;
			};

			default:
				owner->APPDIC->ToolsTrace( TRC_FIELD, TLV_HIGH, "(%d,%d) Invalid type: %d", owner? owner->GetFormatNumber() : -1, number, type );
				RAISEFIELDERROR_3PARAM( ERR_INV_TYPE, GetFieldNumber(), owner? owner->GetFormatNumber() : -1, number, type );
		};
	} else {
		owner->APPDIC->ToolsTrace( TRC_FIELD, TLV_HIGH, "(%d,%d) Invalid type: %d", owner? owner->GetFormatNumber() : -1, number, type );
		RAISEFIELDERROR_3PARAM( ERR_INV_TYPE, GetFieldNumber(), owner? owner->GetFormatNumber() : -1, number, type );
	};

	OUTFUNCRET( result )
};

short int XCField::GetShort() const {
	INFUNC( XCField::GetShort, owner->APPDIC )
	OUTFUNCRET( *(short int*)buffer_offset );
};

bool XCField::GetBool() const {
	INFUNC( XCField::GetBool, owner->APPDIC );

	char bool_char = *buffer_offset;
	if (bool_char != '0' && bool_char != '1')
		RAISEFIELDERROR_1PARAM( ERR_INV_CHAR, GetFieldNumber(), this );

	OUTFUNCRET( *buffer_offset != '0' );
};

long XCField::GetBLOBSize( bool GetAnyway ) const {
	INFUNC( XCField::GetBLOBSize, owner->APPDIC );
	long result = 0;
	if( validblob || GetAnyway )
		result = *(long *)buffer_offset;
	OUTFUNCRET( result );
};

long int *XCField::GetBLOB() const {
	INFUNC( XCField::GetBLOB, owner->APPDIC );
	long int *result = NULL;
	if( validblob )
		result = (long *)*((long **)(buffer_offset + sizeof( long )));
	OUTFUNCRET( result );
};



void XCField::GetBLOBbase64( char *buffer, int maxsize) const {
	INFUNC( XCField::GetBLOBbase64, owner->APPDIC );
	XCBase64 b64;
	int length = GetBLOBSize();

	if( validblob ) {
		if( length * (4/3) > maxsize ) {		//doh! too big
			printf("Warning! Truncating BLOB.\n");
			length = maxsize * (3/4);
		};

		b64.Encode( buffer, (unsigned char *)GetBLOB(), length );
	}

	OUTFUNC();
};

long XCField::GetBLOBbase64Size() const {
	INFUNC( XCField::GetBLOBbase64Size, owner->APPDIC );
	long long result = 0;
	if( validblob ) 
		result = ((*(long *)buffer_offset * 4)/3) + 4;	// + 4 to make sure we have room for three "extra" characters and null termination
	OUTFUNCRET( result );
};


int XCField::CalcStringSize() const {
	char* counter = buffer_offset;
	char* last_space = NULL;

//	printf( "CalcStringSize for '%s'\n", buffer_offset );

	// Count forward
	while (*counter != 0 && counter < buffer_offset + size) {
		if (*counter == ' ') {
			if (!last_space)
				last_space = counter;
		} else
			last_space = NULL;
		counter++;
	};

	// Null terminate the string
	if (last_space) {
		*last_space = 0;
		counter = last_space;
	} else
		*counter = 0;

//	printf( "CalcStringSize result = %d  ('%s')\n", counter - buffer_offset, buffer_offset );
	return counter - buffer_offset;
};



char* XCField::GetString( char* result ) const {
	int tempsize = size;
	char* counter = buffer_offset + size - 1;

	owner->APPDIC->ToolsTrace( TRC_FIELD, TLV_HIGH, "(%d,%d) GetString, len=%d", owner? owner->GetFormatNumber() : -1, number, tempsize );

	// Null terminate the string
	while ((*counter == ' ' || *counter == 0) && counter >= buffer_offset)
		counter--;
	*(counter+1) = 0;

	strcpy( result, buffer_offset );

	return result;
};



const char* XCField::GetStringPtr() const {
	INFUNC( XCField::GetStringPtr, owner->APPDIC )

	if ((data_type_defns[ GetType() ].type_group & TYPE_STRING) == 0)
		RAISEFIELDERROR_1PARAM( ERR_INV_STRING_FIELD, GetFieldNumber(), this );

	OUTFUNCRET( (const char*)buffer_offset );
};



int XCField::Clear( void ) {
	INFUNC( XCField::Clear, owner->APPDIC )
	owner->APPDIC->ToolsTrace( TRC_FIELD, TLV_HIGH, "(%d,%d) SetBlank", owner ? owner->GetFormatNumber() : -1, number );

	if (!read_only) {
		if (type == LITERAL_VAL) {
			// We do special stuff for literals..
			if (flags & 512) {
				// If we allow null literals, then set the blank value
				*(long*)buffer_offset = NOT_IN_LIT_GROUP;
			} else {
				// Otherwise, we need to take the default value (0 may or may not be valid)
				SetDefault();
			};
		} else if (type == LITERAL_STRING) {
			// Take the default value (0 may or may not be valid)
			SetDefault();
		} else if (type == XCBLOB) {
			delete[] *(long **)(buffer_offset + sizeof( long ));
			validblob = false;
			owner->SetContainsBLOB( false );
		} else if (type == XCBOOL && IsBlank()) {
			SetDefault();
		} else
			memset( buffer_offset, 0, allocsize );
		changed = true;
	} else
		RAISEFIELDERROR( ERR_FIELD_READ_ONLY, GetFieldNumber() );

	OUTFUNCRET( 0 )
};


void XCField::SetLong( const int value ) {
	INFUNC( XCField::SetLong, owner->APPDIC );

	owner->APPDIC->ToolsTrace( TRC_FIELD, TLV_HIGH, "(%d,%d) SetLong: %d", owner ? owner->GetFormatNumber() : -1, number, value );

	if (!read_only)
	{
		if ( data_type_defns[ type ].type_group & TYPE_STRING )
			sprintf( buffer_offset, "%d", value );
		else if (type == D_INTEGER || type == LITERAL_VAL)
			memcpy( buffer_offset, &value, sizeof( value ) );
		else if (type == S_INTEGER)
			SetShort( value );
		else if (type == DEC_DOUBLE)
			SetDouble( value );
		else if (type == DEC_FLOAT)
			SetFloat( value );
		else
			RAISEFIELDERROR( 1, GetFieldNumber() );
	}
	else
		RAISEFIELDERROR( ERR_FIELD_READ_ONLY, GetFieldNumber() );

	changed = true;

	OUTFUNC();
};




void XCField::SetLiteral( long value ) {
	INFUNC( XCField::SetLiteral, owner->APPDIC );

	owner->APPDIC->ToolsTrace( TRC_FIELD, TLV_HIGH, "(%d,%d) SetLiteral: %d", owner ? owner->GetFormatNumber() : -1, number, value );

	if (!read_only) {
		if (type == LITERAL_VAL) {
			long result = owner->APPDIC->ToLitGroup( owner->APPDIC->GetLiteral( value ), dec_places );
			if (result != NOT_IN_LIT_GROUP || AllowBlankLiterals()) {
				memcpy( buffer_offset, &result, sizeof( long ));
			} else
				RAISEFIELDERROR( 1, GetFieldNumber() );
		} else {
			XCField* lit = owner->APPDIC->GetLiteral( value );
//			memcpy( buffer_offset, &value, sizeof( value ) );

			if (GetLength() >= lit->GetLength())
				memcpy( buffer_offset, lit->GetOffset(), lit->GetLength() );
			else
				RAISEFIELDERROR( 1, GetFieldNumber() );
		};
	} else
		RAISEFIELDERROR( ERR_FIELD_READ_ONLY, GetFieldNumber() );

	changed = true;

	OUTFUNC();
};


void XCField::SetShort( short int value ) {
	INFUNC( XCField::SetShort, owner->APPDIC );

	owner->APPDIC->ToolsTrace( TRC_FIELD, TLV_HIGH, "(%d,%d) SetShort: %d", owner ? owner->GetFormatNumber() : -1, number, value );

	if (!read_only)
		memcpy( buffer_offset, &value, sizeof( value ) );
	else
		RAISEFIELDERROR( ERR_FIELD_READ_ONLY, GetFieldNumber() );

	changed = true;

	OUTFUNC();
};



void XCField::SetFloat( float pVal ) {
	INFUNC( XCField::SetFloat, owner->APPDIC );

	owner->APPDIC->ToolsTrace( TRC_FIELD, TLV_HIGH, "(%d,%d) SetFloat: %f", owner ? owner->GetFormatNumber() : -1, number, pVal );
	
	if (!read_only)
		memcpy( buffer_offset, &pVal, sizeof( pVal ) );
	else
		RAISEFIELDERROR( ERR_FIELD_READ_ONLY, GetFieldNumber() );

	changed = true;

	OUTFUNC();
};


void XCField::SetDouble( double value ) {
	INFUNC( XCField::SetDouble, owner->APPDIC );

	owner->APPDIC->ToolsTrace( TRC_FIELD, TLV_HIGH, "(%d,%d) SetDouble: %f", owner ? owner->GetFormatNumber() : -1, number, value );

	if (!read_only) {
		if ( data_type_defns[ type ].type_group & TYPE_STRING ) {
			char temp[ 20 ];
			sprintf( temp, "%%%ld.%ldf", size, GetDecPlaces() );
			sprintf( buffer_offset, temp, value );
		} else if (type == DEC_DOUBLE)
			*(double*)buffer_offset = value;
		else if (type == DEC_FLOAT)
			SetFloat( value );
		else if (type == D_INTEGER)
			SetLong( (long)value );
		else if (type == S_INTEGER)
			SetShort( (short int)value );
		else if ( data_type_defns[ type ].type_group & TYPE_DUR )
			DoubleToDuration( value );
		else
			RAISEFIELDERROR( 1, GetFieldNumber() );
	} else
		RAISEFIELDERROR( ERR_FIELD_READ_ONLY, GetFieldNumber() );

	changed = true;

	OUTFUNC();
};


void XCField::SetString( const char* newvalue ) {
	INFUNC( XCField::SetString, owner->APPDIC );

	owner->APPDIC->ToolsTrace( TRC_FIELD, TLV_HIGH, "(%d,%d) SetString to '%s'", owner ? owner->GetFormatNumber() : -1, number, newvalue );

        if (!read_only) {
                // Attempt to convert string to appropriate type
                // TODO: This needs to be integrated with the string_or_numeric move function at some point
                switch( type ) {
                        case S_INTEGER:
                                if (newvalue)
                                        SetShort( atoi( newvalue ) );
                                else
                                        Clear();
                                break;

                        case D_INTEGER:
                        case AUTONUMBER:
                                if (newvalue)
                                        SetLong( atoi( newvalue ) );
                                else
                                        Clear();
                                break;

                        case DEC_DOUBLE:
                                if (newvalue)
                                        SetDouble( atof( newvalue ) );
                                else
                                        Clear();
                                break;

                        case DEC_FLOAT:
                                if (newvalue)
                                        SetFloat( atof( newvalue ) );
                                else
                                        Clear();
                                break;

                        case LITERAL_VAL: {
                                long result;
                                bool isblank;

                                if (newvalue) {
                                        // skip whitespace
                                        while (*newvalue && *newvalue == ' ')
                                                newvalue++;

                                        result = owner->APPDIC->ToLitGroup( newvalue, dec_places );
                                        isblank = (*newvalue==0);
                                } else {
                                        result = NOT_IN_LIT_GROUP;
                                        isblank = true;
                                };

                                if (result != NOT_IN_LIT_GROUP || (isblank && AllowBlankLiterals())) {
                                        // Set the literal value
                                        memcpy( buffer_offset, &result, sizeof( long ));
                                } else {
                                        RAISEFIELDERROR( ERR_INV_LIT_VAL_FOR_GROUP, GetFieldNumber() );
                                };
                                break;
                        };

                        default:
                                if (newvalue) {
                                        truncated = !UTF8ToUTF8( newvalue, buffer_offset, allocsize, size );
                                } else
                                        Clear();
                                changed = true;
                                break;
                };
        } else
                RAISEFIELDERROR( ERR_FIELD_READ_ONLY, GetFieldNumber() );

	OUTFUNC();
};


void XCField::SetBool( const bool value ) {
	INFUNC( XCField::SetBool, owner->APPDIC );

	// Ensure that this is a boolean type
	if (type != XCBOOL)
		RAISEFIELDERROR( 1, GetFieldNumber() );

	// Check for locked fields
	if (!read_only) {
		*buffer_offset = value ? '1' : '0';
		changed = true;
	} else
		RAISEFIELDERROR( ERR_FIELD_READ_ONLY, GetFieldNumber() );

	OUTFUNC();
};

void XCField::InitBLOB( long size ) {
	INFUNC( XCField::InitBLOB, owner->APPDIC );

	long int *data;

	// Ensure that this is a BLOB type
	if (type != XCBLOB )
		RAISEFIELDERROR( 1, GetFieldNumber() );

	// Check for locked field
	if (read_only)
		RAISEFIELDERROR( ERR_FIELD_READ_ONLY, GetFieldNumber() );

	// Delete old pointer if necessary
	if (validblob)
		delete[] *(long **)(buffer_offset + sizeof( long ));

	// Allocate data for BLOB and store size and pointer in buffer
	if (size) {
		data = (long *)malloc(size);

		// Mark flag to delete the BLOB when necessary
		validblob = true;
	} else {
		data = NULL;
		validblob = false;
	};
	owner->SetContainsBLOB( true );
	memcpy( buffer_offset, &size, sizeof(long) );
	memcpy( buffer_offset + sizeof( long ), &data, sizeof(long *) );

	OUTFUNC();
};

void XCField::SetBLOB( long size, long int *data ) {
	INFUNC( XCField::SetBLOB, owner->APPDIC );

	if( size > 0 ) {
		// Init the BLOB
		InitBLOB( size );

		// And put memory into it
		memcpy( *(long**)(buffer_offset + sizeof( long )), data, size );
	}

	OUTFUNC();
};
		
void XCField::SetBLOBbase64( const long size, const char *base64 ) {
	INFUNC( XCField::SetBLOBbase64, owner->APPDIC );
	XCBase64 b64;

	if( size > 0 ) {
		long newsize = (long)((size * 3)/4) + 1;
		if( base64[size-1] == '=' ) newsize--;		//see if it is padded on the end
		if( base64[size-2] == '=' ) newsize--;
		InitBLOB( newsize );
	
		b64.Decode( base64, (unsigned char *)GetBLOB(), size );
	} else {
		// There is no blob
		InitBLOB( 0 );
	};

	OUTFUNC();
};

void XCField::SetCurrentDate() {
	INFUNC( XCField::SetCurrentDate, owner->APPDIC );

	if (!read_only) {
		tm *timeval;
		time_t stime;

		time( &stime );
		timeval = localtime( &stime );

		switch( type ) {
			case DATE_MDY:
				sprintf( buffer_offset, "%2.2d%2.2d%2.2d",
					timeval->tm_mon + 1,
					timeval->tm_mday,
					timeval->tm_year % 100 );
				break;

			case DATE_MDY_DISPLAY:
				sprintf( buffer_offset, "%2.2d/%2.2d/%2.2d",
					timeval->tm_mon + 1,
					timeval->tm_mday,
					timeval->tm_year % 100 );
				break;

			case DATE_MDCY_DISPLAY:
				sprintf( buffer_offset, "%2.2d/%2.2d/%4.4d",
					timeval->tm_mon + 1,
					timeval->tm_mday,
					timeval->tm_year+1900 );
				break;

			// TODO: FIX ME LATER
/*
			case DATE_YMD				=	11; // YYMMDD
			case DATE_YMD_DISPLAY		=	12; // YY/MM/DD
			case DATE_DMY				=	13; // DDMMYY
			case DATE_DMY_DISPLAY		=	14; // DD/MM/YY
			case DATE_STR_DISPLAY		=	15; // MONTH DAY, YEAR  EX: MAY 1, 1980
			case DATE_MD				=	16; // MMDD
			case DATE_MD_DISPLAY		=	17; // MM/DD
			case DATE_DM				=	18; // DDMM
			case DATE_DM_DISPLAY		=	19; // DD/MM
				break;
*/
			case DATE_DB:
				sprintf( buffer_offset, "%4.4d-%2.2d-%2.2d",
					timeval->tm_year+1900,
					timeval->tm_mon + 1,
					timeval->tm_mday );
				break;

			case TIME_24HR_DB:
				sprintf( buffer_offset, "%2.2d:%2.2d:%2.2d",
					timeval->tm_hour,
					timeval->tm_min,
					timeval->tm_sec );
				break;

			case DATE_TIMESTAMP:
				sprintf( buffer_offset, "%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d",
					timeval->tm_year+1900,
					timeval->tm_mon + 1,
					timeval->tm_mday,
					timeval->tm_hour,
					timeval->tm_min,
					timeval->tm_sec );
				break;

			default:
				owner->APPDIC->ToolsTrace( TRC_FIELD, TLV_EVERYTHING, "Invalid date format: %d", type );
				RAISEFIELDERROR_1PARAM( ERR_INV_DATE_FIELD, GetFieldNumber(), this );
		};

		changed = true;
	} else
		RAISEFIELDERROR( ERR_FIELD_READ_ONLY, GetFieldNumber() );

	owner->APPDIC->ToolsTrace( TRC_FIELD, TLV_EVERYTHING, "Current date: %s", buffer_offset );
	
	OUTFUNC();
};



int XCField::LoadStructureData( buffer_ds *new_field_info, XCBuffer *newowner ) {
	assert( newowner );

//	INFUNC( XCField::LoadStructureData, newowner->APPDIC );

	owner = newowner;
	mydesc = new_field_info;

	field_descr = (buffer_field*)owner->APPDIC->FixOffset( mydesc->field_offset );
	assert( field_descr );

	// Load the fields info from the structure
	number		= field_descr->number;
	desc_message = field_descr->desc_message;

	buffer_offset = (char*)mydesc->data_offset;
//	Position	= mydesc->data_offset;
	dec_places	= mydesc->dec_places;
	type		= mydesc->data_type;
	min_len		= mydesc->min_length;
	size		= mydesc->max_length;
	flags		= mydesc->flags;
	allocsize	= mydesc->alloc_size;

//	owner->APPDIC->ToolsTrace( TRC_FIELD, TLV_HIGH, "(%d,%d) Load field data_structure information", owner ? owner->GetFormatNumber() : -1, number );

//	OUTFUNC();
	return number;
};


bool XCField::IsBlank() const {
	INFUNC( XCField::IsBlank, owner->APPDIC )

	// If not a string type, always return false
	long type = data_type_defns[ GetType() ].type_group;
	if ( (type & (TYPE_STRING | TYPE_DATE | TYPE_TIME | TYPE_DUR)) == 0) {
		// Specific test for literal values
		if (GetType() == LITERAL_VAL) {
			// For literals, check to see if blank
			RETURNFUNC( GetLong() == NOT_IN_LIT_GROUP );
		} else
			RETURNFUNC( false );
	};

	// Check for any non-blank characters
	char* temp = buffer_offset;
	int i = 0;
	while (i < size) {
		if (*temp == 0)
			RETURNFUNC( true )
		else if (*temp == '"') {		// Check for explicit blank character
			if (temp != buffer_offset)
				RETURNFUNC( false );
		} else if (*temp != ' ') {
			RETURNFUNC( false );
		};

		temp++;
		i++;
	};

	OUTFUNCRET( true );
};




bool XCField::IsExplicitBlank() const {
	INFUNC( XCField::IsBlank, owner->APPDIC )

	if (type == S_INTEGER || type == D_INTEGER) {
		RETURNFUNC( false );
	};

	char* temp = buffer_offset;
	if (*temp == '"') {
		int i = 0;
		while (i < size) {
			if (*temp == 0)
				RETURNFUNC( true )
			else if (*temp != ' ')
				RETURNFUNC( false );

			temp++;
			i++;
		};
	} else
		RETURNFUNC( false );

	OUTFUNCRET( true );
};



bool XCField::IsChanged() const {
	return changed;
};


void XCField::SetChange( bool new_change ) {
	changed = new_change;
};


void XCField::MoveField( const XCField *newfield, bool MoveBlanks ) {
	INFUNC( XCField::MoveField, owner->APPDIC )

	// Ensure that we were sent a valid field
	if (!newfield)
		RAISETOOLSERROR_1PARAM( ERR_INVALID_PARAMETER, 1 );

	assert( field_descr );

	owner->APPDIC->ToolsTrace( TRC_FIELD, TLV_HIGH, "(%d,%d) MoveField - %-16.16s  from type:%d ==> %d", owner ? owner->GetFormatNumber() : -1, number, field_descr->name, newfield->GetType(), GetType());

	if (read_only)
		RAISEFIELDERROR( ERR_FIELD_READ_ONLY, GetFieldNumber() );

	// Check to see if we can convert types
	if (MIN_TYPE <= newfield->type && newfield->type <= MAX_TYPE) {
		// We have a good source type
		if (type != newfield->type) {
			// Not like types
			if (MIN_TYPE <= type && type <= MAX_TYPE ) {
				// Good target type, so check to see if valid conversion
				if (!conversion_tbl_lookup( newfield->type ))
					RAISEFIELDERROR_4PARAM( ERR_INVALID_CONVERSION, GetFieldNumber(), newfield, newfield, this, this );
			} else
				RAISEFIELDERROR_1PARAM( ERR_INV_TARGET, GetFieldNumber(), this );
		};
	} else
		RAISEFIELDERROR_1PARAM( ERR_INV_SOURCE, GetFieldNumber(), newfield );


	const tdata_type_defnition *source_dd = &data_type_defns[ newfield->type ];
	const tdata_type_defnition *target_dd = &data_type_defns[ type ];



	// We know that we have a valid conversion path, now check source data
	switch( newfield->type ) {
		case BIG_STRING:
			// VALIDATE BIG_STRING TYPE FOR NON-GROUP
			// ITEMS
//				if (!newfield->group_flag)
				if (!newfield->Valid_String())
					RAISEFIELDERROR_1PARAM( ERR_INV_BIG_STRING, GetFieldNumber(), newfield );
			break;

		case STRING:
			// VALIDATE STRING TYPE FOR NON-GROUP ITEMS
//				if (!newfield->group_flag)
				if (!newfield->Valid_String())
					RAISEFIELDERROR_1PARAM( ERR_INV_CHAR, GetFieldNumber(), newfield );
			break;

		case ALPHA_STRING:
			if (!newfield->Valid_Alpha_Str())
				RAISEFIELDERROR_1PARAM( ERR_INV_ALPHA_STRING, GetFieldNumber(), newfield );
			break;

		case DISPLAY_NUM:
			if (!newfield->IsBlank() && !newfield->Chk_Display()) 
				RAISEFIELDERROR_1PARAM( ERR_INV_NON_NUM, GetFieldNumber(), newfield );
			break;

		default:
			if (source_dd->type_group & TYPE_TIME)
				if (!newfield->Chk_Time( newfield->buffer_offset, newfield->size, newfield->type ))
					RAISEFIELDERROR_1PARAM( ERR_INV_TIME, GetFieldNumber(), newfield->GetFieldName() );
	}

	// We are finally ready to do the move!!


	// Check for non-like types
	if (type != newfield->type) {
		if ((source_dd->type_group & TYPE_STRING || source_dd->type_group & TYPE_NUMERIC) &&
			(target_dd->type_group & TYPE_STRING || target_dd->type_group & TYPE_NUMERIC))
			string_or_numeric_move( newfield, MoveBlanks );
		else if (source_dd->type_group & TYPE_DATE && target_dd->type_group & TYPE_DATE)
			date_move( newfield, MoveBlanks );
		else if (source_dd->type_group & TYPE_TIME && target_dd->type_group & TYPE_TIME)
			time_move( newfield, MoveBlanks );
		else
			duration_move( newfield, MoveBlanks );
	} else {
		// Like source and target types
		if (target_dd->type_group & TYPE_STRING || target_dd->type_group & TYPE_NUMERIC)
			string_or_numeric_move( newfield, MoveBlanks );
		else if (target_dd->type_group & TYPE_BLOB)
			BLOB_move( newfield, MoveBlanks );
		else if (target_dd->type_group & TYPE_DATE)
			date_move( newfield, MoveBlanks );
		else if (target_dd->type_group & TYPE_TIME)
			time_move( newfield, MoveBlanks );
		else
			duration_move( newfield, MoveBlanks );
	};

	OUTFUNC();
};




bool XCField::CheckForDecimals() {
	double value = GetDouble( true );
	return (value != trunc( value ));
//	return truncated;
};





//
// Set the field to it's default value
//
void XCField::SetDefault() {
	INFUNC( XCField::SetDefault, owner->APPDIC );

	const void* data = owner->APPDIC->FixOffset( owner->mydesc->default_offset ) + mydesc->data_offset;

	// Make sure that we have a default value
	if (owner->mydesc->default_offset) {
		// Locate the buffer's default value space and copy over our info
		memcpy( buffer_offset, data, mydesc->alloc_size );

		if (field_descr->type == LITERAL_STRING && XCGetLanguage() != 0) {
			long language = XCGetLanguage();
			XCSetLanguage(0);
			long lit = owner->APPDIC->ToLitGroup( this, field_descr->decimal_places );
			XCSetLanguage( language );

			XCField* lit_field = owner->APPDIC->FromLitGroup( lit, field_descr->decimal_places );
			if (!lit_field)
				RAISEFIELDERROR( ERR_INV_LIT_VAL_FOR_GROUP, GetFieldNumber() );
			MoveField( lit_field, true );
		};

		// Mark that this field has a default value
		user_entered = false;
		changed = false;
	} else
		RAISEFIELDERROR( ERR_INV_FIELD_DEFAULT, GetFieldNumber() );

	OUTFUNC();
};




void XCField::Uppercase() {
	INFUNC( XCField::Uppercase, owner->APPDIC );

	// Check for a string type
	if (data_type_defns[ GetType() ].type_group & TYPE_STRING) {
		char* temp = GetOffset();
		long count = GetLength();
		while (*temp && count) {
			*temp = toupper( *temp );
			temp++;
			count--;
		};
	};

	OUTFUNC();
};



void XCField::Lowercase() {
	INFUNC( XCField::Lowercase, owner->APPDIC );

	// Check for a string type
	if (data_type_defns[ GetType() ].type_group & TYPE_STRING) {
		char* temp = GetOffset();
		long count = GetLength();
		while (*temp && count) {
			*temp = tolower( *temp );
			temp++;
			count--;
		};
	} else
		RAISEFIELDERROR_1PARAM( ERR_INV_STRING_FIELD, GetFieldNumber(), this );

	OUTFUNC();
};





int XCField::charoutput( char* temp, int max_size, bool xml_convert ) const {
	INFUNC( XCField::charoutput, owner->APPDIC );

	int resultlen = 0;

	const XCField* target = this;
	long BLOBbase64size;
	char *BLOBbase64;

	switch( GetType() ) {
		case AUTONUMBER:
		case S_INTEGER:
		case D_INTEGER:
			resultlen = snprintf( temp, max_size, "%d", GetLong( true ) );
			break;

		case DEC_FLOAT:
		case DEC_DOUBLE: {
			char ftemp[ 80 ];
//			sprintf( ftemp, "%%%ld.%ldf", size, GetDecPlaces() );
			sprintf( ftemp, "%%.%ldf", GetDecPlaces() );
			resultlen = snprintf( temp, max_size, ftemp, GetDouble( true ) );
			break;
		};

		case DATE_JULIAN:
			resultlen = snprintf( temp, max_size, "%e", GetDouble( true ) );
			break;

		case XCBLOB:
			BLOBbase64size = GetBLOBbase64Size();
			if(BLOBbase64size > 0 ) {
				BLOBbase64 = new char[BLOBbase64size];
				GetBLOBbase64( BLOBbase64, BLOBbase64size );
				strncpy( temp, BLOBbase64, max_size );
				delete [] BLOBbase64;
			}
			break;

		case LITERAL_VAL:
			if (GetLong() == NOT_IN_LIT_GROUP) {
				strcpy( temp, "" );
			} else {
				target = owner->APPDIC->FromLitGroup( GetLong(), GetDecPlaces() );
				if (!target) {
					RAISEFIELDERROR( ERR_INV_LIT_VAL_FOR_GROUP, GetFieldNumber() );
				};
			};
			// Fall through to string stuff

		default:
			const char* input = (const char*)target->buffer_offset;
			char* output = temp;
			char* lastspace = NULL;
			long count = max_size;
			while (*input && count) {
				if (*input < 27) {
					// Ignore control characters?
				} else if (*input == '\'' && xml_convert) {
					*output++ = '&';
					*output++ = 'a';
					*output++ = 'p';
					*output++ = 'o';
					*output++ = 's';
					*output++ = ';';
					lastspace = NULL;
					count -= 6;
				} else if (*input == '"' && xml_convert) {
					*output++ = '&';
					*output++ = 'q';
					*output++ = 'u';
					*output++ = 'o';
					*output++ = 't';
					*output++ = ';';
					lastspace = NULL;
					count -= 6;
				} else if (*input == '<' && xml_convert) {
					*output++ = '&';
					*output++ = 'l';
					*output++ = 't';
					*output++ = ';';
					lastspace = NULL;
					count -= 4;
				} else if (*input == '>' && xml_convert) {
					*output++ = '&';
					*output++ = 'g';
					*output++ = 't';
					*output++ = ';';
					lastspace = NULL;
					count -= 4;
				} else if (*input == '&' && xml_convert) {
					*output++ = '&';
					*output++ = 'a';
					*output++ = 'm';
					*output++ = 'p';
					*output++ = ';';
					lastspace = NULL;
					count -= 5;
				} else if (*input == ' ') {
					if (!lastspace)
						lastspace = output;
					*output++ = ' ';
					count--;
				} else {
					*output = *input;
					output++;
					lastspace = NULL;
					count--;
				};

				input++;
			};
			if (lastspace) {
				resultlen = lastspace - temp;
				*lastspace = 0;
			} else {
				resultlen = output - temp;
				*output = 0;
			};

			break;
	};  // switch

	OUTFUNCRET( resultlen );
};


void XCField::OutputTransport( XCTransport* transport, char* temp ) const {
	char *BLOBbase64;
	long BLOBbase64size;
	if( GetType() == XCBLOB ) {
		BLOBbase64size = GetBLOBbase64Size();
		if(BLOBbase64size > 0 ) {
			BLOBbase64 = new char[BLOBbase64size];
			GetBLOBbase64( BLOBbase64, BLOBbase64size );
			transport->Send( BLOBbase64 );
			delete [] BLOBbase64;
		}
	} else {
		int result = charoutput( temp, 255, true );
		if (result)
			transport->Send( temp );
	}
};



bool XCField::IsTrue() const {
	INFUNC( XCField::IsTrue, owner->APPDIC );

	if (type == XCBOOL) {
		RETURNFUNC( GetBool() );
	} else if (type == S_INTEGER || type == D_INTEGER) {
		RETURNFUNC( GetLong(true) != 0 )
	} else
		RAISEFIELDERROR( 1, GetFieldNumber() );

	OUTFUNC();
};
